package ferp.core.ai.nn;

import ferp.center.network.request.RequestBidAdd;
import ferp.core.calc.scorer.Passing;
import ferp.core.card.Card;
import ferp.core.game.Bid;
import ferp.core.game.Game;
import ferp.core.game.Settings;

import java.util.Arrays;

/**
 * User: igorgok
 * Date: 12/9/12 1:45 PM
 */
public class Adaptation
{
  // bid NN configuration
  // cards(32) + #hand(2) + starting tricks(2) + last bid(2) + penalty(1)
  public static final int BID_NN_NUM_OF_INPUTS = 32 + 2 + 2 + 2 + 1;
  // bid result(1)
  public static final int BID_NN_NUM_OF_OUTPUTS = 1;

  public static int getPenalty(Game g, Settings settings) {return Passing.getTrickCost(g, settings);}
  public static char getLastBid(int hand, Bid gameCurrentBid) {return (hand == 0 ? RequestBidAdd.NONE : (gameCurrentBid == null ? RequestBidAdd.PASS : RequestBidAdd.type(gameCurrentBid.type)));}

  public static char getResult(double r) {return r <= 0.5 ? RequestBidAdd.PASS : RequestBidAdd.PLAY;}

  // create NN training sample from bid data
  public static double[] bid2nnts(int cards, int hand, int st, char lb, int penalty, char result)
  {
    // create the sample and populate inputs
    double[] sample = bid2nni(cards, hand, st, lb, penalty, BID_NN_NUM_OF_INPUTS + BID_NN_NUM_OF_OUTPUTS);

    // output
    sample[BID_NN_NUM_OF_INPUTS] = bid2nno(result);

    return sample;
  }

  // create an exact NN input from the bid data
  public static double[] bid2nni(int cards, int hand, int st, char lb, int penalty)
  {
    // create the sample and populate inputs
    return bid2nni(cards, hand, st, lb, penalty, BID_NN_NUM_OF_INPUTS);
  }

  // whist1 NN configuration
  // cards(4) + contract suit(1) + contract tricks(1)
  //private static final int WHIST_NN1_NUM_OF_INPUTS = 0;
  // whist result(1)
  //private static final int WHIST_NN1_NUM_OF_OUTPUTS = 1;

  // whist2 NN configuration
  // cards(4) + #hand(1) + contract suit(1) + starting tricks(1) + last bid(1) + penalty(1)
  //private static final int WHIST_NN2_NUM_OF_INPUTS = 0;
  // whist result(1)
  //private static final int WHIST_NN2_NUM_OF_OUTPUTS = 1;

  private static double[] bid2nni(int cards, int hand, int st, char lb, int penalty, int size)
  {
    double[] inputs = new double[size];
    int[] cbs = {Card.Set.get(cards, Card.Suit.SPADES), Card.Set.get(cards, Card.Suit.CLUBS), Card.Set.get(cards, Card.Suit.DIAMONDS), Card.Set.get(cards, Card.Suit.HEARTS)};
    int i = 0;

    Arrays.sort(cbs);

    // cards in hand
    for (int s : cbs)
      for (int c = 128; c > 0; c >>>= 1)
        inputs[i++] = (s & c) != 0 ? 1 : 0;

    // hand
    //inputs[i++] = hand2input(hand);
    i = hand2input(hand, inputs, i);
    // starting tricks
    //inputs[i++] = st2input(st);
    i = st2input(st, inputs, i);
    // last bid: NONE, PASS, PLAY, MISERE
    //inputs[i++] = lb2input(lb);
    i = lb2input(lb, inputs, i);
    // penalty
    inputs[i] = penalty2input(penalty, 6);

    return inputs;
  }

  private static double bid2nno(char result)
  {
    // output: PASS or PLAY (misere is considered PASS!)
    return (result == RequestBidAdd.PLAY ? 1 : 0);
  }

  private static int hand2input(int hand, double[] inputs, int index)
  {
    inputs[index++] = hand & 1;
    inputs[index++] = hand & 2;

    return index;
  }

  private static int st2input(int st, double[] inputs, int index)
  {
    inputs[index++] = st & 1;
    inputs[index++] = st & 2;

    return index;
  }

  private static int lb2input(char lb, double[] inputs, int index)
  {
    // NONE - 0,0
    // PASS - 1,0
    //
    inputs[index++] = (lb == RequestBidAdd.PASS || lb == RequestBidAdd.MISERE) ? 1 : 0;
    inputs[index++] = (lb == RequestBidAdd.PLAY || lb == RequestBidAdd.MISERE) ? 1 : 0;

    return index;
  }

  private static double penalty2input(int penalty, int limit)
  {
    // limit by 'limit'
    if (penalty > limit)
      penalty = limit;

    // [1..limit] -> [0..1]
    return (double)(penalty - 1) / (double)(limit - 1);
  }
}
